package com.sky.wsp.sql.scanner.controller;

import cn.hutool.core.io.FileUtil;
import com.sky.wsp.sql.scanner.patterns.CommonPatternManager;
import com.sky.wsp.sql.scanner.utils.*;
import com.sky.wsp.sql.scanner.vo.MatchResult;
import com.sky.wsp.sql.scanner.vo.ScanResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @author wangshaopeng@talkweb.com.cn
 * @desccription 在此输入描述
 * @date 2024/11/14
 */
@RestController
public class ScanController {

    /**
     * 第1步：解压jar/war/tar
     */
    @GetMapping("un-pack")
    public List<String> unPack(String path) {
        List<String> unPackages = new ArrayList<>();
        File[] files = MyFileUtil.listPackFiles(path);
        if (null == files || 0 == files.length) {
            unPackages.add("指定文件不存在，或指定目录下没有文件！");
            return unPackages;
        }
        unPackages.add("解压路径如下：");
        for (int i = 0; i < files.length; i++) {
            File source = files[i];
            File target = MyFileUtil.unPackageDir(source);
            HutoolCompressUtil.unZipToDir(source, target, false);
            unPackages.add(target.getAbsolutePath());
        }
        return unPackages;
    }

    /**
     * 第2步：列出所有依赖的jar，人工判断是否需要进行反编译
     */
    @GetMapping("libs")
    public Map<String, List<String>> libs(String path, Boolean useIgnoreFilter) {
        if (null == useIgnoreFilter) {
            useIgnoreFilter = true;
        }
        Map<String, List<String>> result = new HashMap<>();
        File unzipDir = new File(path);
        if (!unzipDir.exists()) {
            result.put("指定目录不存在！", null);
            return result;
        }
        if (!unzipDir.isDirectory()) {
            result.put("只能是目录！", null);
            return result;
        }

        List<String> deepJarList = new ArrayList<>();
        HutoolCompressUtil.deepJarList(unzipDir, deepJarList, useIgnoreFilter);
        deepJarList.forEach(System.out::println);

        System.out.println("\n\n如果不需要解压，可以按下面格式，将关键字添加到" + ConfigFileConst.IGNORE_JAR_KEYS + "进行忽略");
        ArrayList<String> keys = FileNameUtil.getIgnoresKeyByNames(deepJarList);
        keys.forEach(System.out::println);
        System.out.println("\n\n\n\n");

        List<String> list = new ArrayList<>(deepJarList.size() + keys.size() + 9);
        list.addAll(deepJarList);
        list.add("");
        list.add("");
        list.add("");
        list.add("");
        list.add("如果不需要解压，可以按下面格式，将关键字添加到" + ConfigFileConst.IGNORE_JAR_KEYS + "进行忽略");
        list.addAll(keys);
        result.put(unzipDir.getAbsolutePath(), list);
        return result;
    }

    /**
     * 第3步：解压lib依赖的jar，自动过滤jar包模式
     */
    @GetMapping("un-pack-libs")
    public Map<String, List<String>> unPackLibs(String path) {
        Map<String, List<String>> result = new HashMap<>();
        File unzipDir = new File(path);
        if (!unzipDir.exists()) {
            result.put("指定目录不存在！", null);
            return result;
        }
        if (!unzipDir.isDirectory()) {
            result.put("只能是目录！", null);
            return result;
        }
        List<String> unZipDirs = new ArrayList<>();
        HutoolCompressUtil.deepJarUnZipToDir(unzipDir, false, unZipDirs);
        result.put(unzipDir.getAbsolutePath(), unZipDirs);
        return result;
    }

    /**
     * 第3步：解压lib依赖的jar，人工指定jar包模式
     */
    @GetMapping("un-pack-libs-by-user")
    public String unPackLibsByUser(String path) {
        File source = new File(path);
        if (!source.exists() && source.isDirectory()) {

        }
        File target = MyFileUtil.unPackageDir(source);
        String result = HutoolCompressUtil.unZipToDir(source, target, false);
        return "解压结果" + result;
    }


    /**
     * 第4步：反编译class
     */
    @GetMapping("decompile")
    public List<String> decompile(String path, String tomcatLibPath) {
        List<String> result = new ArrayList<>();
        File unzipFile = new File(path);
        if (!unzipFile.exists()) {
            result.add("指定文件不存在，或指定目录下没有文件！");
            return result;
        }
        File sourceDir = new File(unzipFile.getAbsolutePath() + "-source");
        Map<String, File> map = new HashMap<>();
        MyFileUtil.findByName(unzipFile, map);
        File classesPath = map.get("classes");
        if (null == classesPath) {
            classesPath = unzipFile;
        }
        result.add("===============classes目录：" + classesPath.getAbsolutePath());
        JdUtil jdUtil = JdUtil.build().addURL(classesPath);

        File libPath = map.get("lib");
        if (null != libPath && libPath.exists()) {
            jdUtil.addJar(libPath);
        }
        File tomcatLibDir = new File(tomcatLibPath);
        if (tomcatLibDir.exists()) {
            jdUtil.addJar(tomcatLibDir);
        }

        int classesPathLength = classesPath.getAbsolutePath().length() + 1;
        MyFileUtil.decompileClasses(classesPath, classesPathLength, jdUtil, sourceDir);
        result.add("classes目录反编译完成：" + sourceDir.getAbsolutePath());
        result.add("");
        result.add("");
        result.add("");
        result.add("");
        if (null != libPath && libPath.exists()) {
            result.add("===============libJar目录：" + libPath.getAbsolutePath());
            MyFileUtil.decompileLibJar(libPath, jdUtil, sourceDir, result);
        } else {
            result.add("===============没有找到lib目录！！！");
        }
        return result;
    }


    /**
     * 第5步：SQL扫描
     */
    @GetMapping("scan")
    public List<Object> scan(String path, String sourceDbType, String targetDbType) {
        List<Object> result = new ArrayList<>();
        File sourcePath = new File(path);
        if (!sourcePath.exists() || !sourcePath.isDirectory()) {
            result.add("指定源码目录不存在！");
            return result;
        }
        // 扫描解压目录，例如java、xml、php、python等，解压即有源文件
        doScan(sourcePath, result, sourceDbType, targetDbType);
        return result;
    }

    /**
     * 根据文件扩展名，进行不同的规则扫描
     */
    private void doScan(File unzipFile, List<Object> result, String sourceDbType, String targetDbType) {
        Set<String> extentions = MyFileUtil.getLinesAsSet(ConfigFileConst.FILE_EXTENTION);
        Set<String> ignoreNames = MyFileUtil.getLinesAsSet(ConfigFileConst.IGNORE_FILE_KEYS);
        Map<String, Pattern> sqlPatterns = CommonPatternManager.getPatterns(ConfigFileConst.SQL_PATTERN);
        String sqlKeysPattern = ConfigFileConst.getSqlKeysPattern(sourceDbType, targetDbType);
        Map<String, Pattern> sqlKeyPatterns = CommonPatternManager.getPatterns(sqlKeysPattern);

        List<ScanResult> resultList = new ArrayList<>();
        scanDeep(unzipFile, resultList,
                extentions, ignoreNames, sqlPatterns, sqlKeyPatterns);

        int sqlCountSum = resultList.stream().mapToInt(r -> r.getSqlCount()).sum();
        int sqlKeyCountSum = resultList.stream().mapToInt(r -> r.getSqlKeyCount()).sum();

        result.add("-----------------↓↓↓↓开始打印：" + unzipFile.getAbsolutePath());
        result.add("扫描结果：含SQL的文件数：" + resultList.size());
        result.add("SQL或数据库CRUD接口个数: " + sqlCountSum);
        if (null == sqlKeysPattern) {
            result.add("未指定源/目标数据库类型，不进行关键字兼容性扫描！ ");
        } else {
            result.add(sourceDbType + "到" + targetDbType + "不兼容关键字出现次数: " + sqlKeyCountSum);
        }
        result.add("-----------------↓↓↓↓下面打印详细结果：");
        result.add(resultList);
        result.add("");
        result.add("");
        result.add("");
        result.add("");
        result.add("");
    }

    private void scanDeep(File dir, List<ScanResult> resultList,
                          Set<String> extentions,
                          Set<String> ignoreNames,
                          Map<String, Pattern> sqlPatterns,
                          Map<String, Pattern> sqlKeyPatterns) {
        File[] files = dir.listFiles();
        if (null == files) {
            return;
        }
        for (File file : files) {
            String fileName = file.getName();
            // 要忽略的文件或文件夹,pom.xml、dist、WEB-INF等
            boolean match = FileNameUtil.prefixMatch(fileName, ignoreNames);
            if (match) {
                continue;
            }
            if (file.isDirectory()) {
                scanDeep(file, resultList, extentions, ignoreNames, sqlPatterns, sqlKeyPatterns);
                continue;
            }
            int dot = fileName.lastIndexOf(".");
            if (dot < 0) {
                // 没有扩展名
                continue;
            }
            String ext = fileName.substring(dot);
            if (!extentions.contains(ext)) {
                continue;
            }
            String content = FileUtil.readString(file, Charset.defaultCharset());
            List<MatchResult> sqlMatchResult = CommonPatternManager.match(content, sqlPatterns);
            List<MatchResult> sqlKeyMatchResult = CommonPatternManager.match(content, sqlKeyPatterns);
            if (sqlMatchResult.isEmpty() && sqlKeyMatchResult.isEmpty()) {
                continue;
            }
            ScanResult scanResult = new ScanResult();
            scanResult.setFileName(file.getAbsolutePath());
            scanResult.setSqlCount(sqlMatchResult.stream().mapToInt(r -> r.getCount()).sum());
            scanResult.setSqlMatchResult(sqlMatchResult);
            scanResult.setSqlKeyCount(sqlKeyMatchResult.stream().mapToInt(r -> r.getCount()).sum());
            scanResult.setSqlKeyMatchResult(sqlKeyMatchResult);
            resultList.add(scanResult);
            System.out.println("扫描完成：" + file.getAbsolutePath() + ", sqlCount: " + scanResult.getSqlCount() + ", sqlKeyCount: " + scanResult.getSqlKeyCount());
        }
    }
}
